
#include <stdio.h>
#include <BList>
#include <BSocket>
#include <BTcpSocket>
#include "member_BSocket.h"
#include "member_BTcpServer.h"
#include "member_BObject.h"

using namespace BWE;

extern BList<BSocket*>		app_sockets;

#ifdef linux
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

void App_SocketStart()
{
	if (app_sockets.empty())
	{

	}
}
void App_SocketClean()
{
	if (app_sockets.empty())
	{

	}
}
void App_SelectSocket(double timeout)
{
	if (app_sockets.empty())
		return;

	fd_set readfds;
	fd_set writefds;
	fd_set exceptfds;

	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&exceptfds);

	int maxfds = 1;
	int count = 0;
	BSocket* sockets[64];
	for (int i = 0; i < app_sockets.size(); i++)
	{
		if (i >= 64)
			break;
		BSocket* socket = app_sockets[i];
		if (socket_member(socket)->state == BSocket::State_Closed)
			continue;
		sockets[count++] = socket;
		int sock = socket_member(socket)->sock;
		FD_SET(sock, &readfds);
		FD_SET(sock, &writefds);
		FD_SET(sock, &exceptfds);
		if(maxfds <= sock)
			maxfds = sock + 1;
	}
	if (count == 0)
	{
		return;
	}

	timeval tv;
	tv.tv_sec = (long)timeout;
	tv.tv_usec = (long)(timeout - tv.tv_sec * 1000);

	int ret = ::select(maxfds, &readfds, &writefds, &exceptfds, &tv);
	if (ret == -1)
	{
		perror("select");
		return;
	}
	if (ret == 0)
	{
		return;
	}

	for (int i = 0; i < count; i++)
	{
		BSocket* socket = sockets[i];
		int sock = socket_member(socket)->sock;
		if (FD_ISSET(sock, &readfds))
		{
			if (socket->type() == BSocket::TcpServer)
			{
				sockaddr_in addr;
				memset(&addr, 0, sizeof(addr));
				socklen_t addrSize = sizeof(addr);

				int tcpsock = ::accept(sock, (sockaddr*)&addr, &addrSize);
				if (sock == 0)
				{
					socket_member(socket)->checkError();
					socket_member(socket)->state = BSocket::State_Closed;
					object_member(socket)->emit(Signal_StateChanged, BSocket::State_Closed);
				}
				else
				{
					BTcpSocket* tcpSocket = new BTcpSocket();
					socket_member(tcpSocket)->sock = tcpsock;
					socket_member(tcpSocket)->addr = addr;
					{
						char cstr[64];
						inet_ntop(AF_INET, &(addr.sin_addr.s_addr), cstr, 63);
						int port = ntohs(addr.sin_port);
						socket_member(tcpSocket)->address = cstr;
						socket_member(tcpSocket)->port = ntohs(addr.sin_port);
					}
					socket_member(tcpSocket)->state = BSocket::State_Connected;
					object_member(socket)->emit(Signal_StateChanged, BSocket::State_Connected);
					tcpserver_member(socket)->newConnection = tcpSocket;
					object_member(socket)->emit(Signal_NewConnection, tcpSocket);
				}
			}
			else
			{
				char buf[2];
				int ret = ::recv(sock, buf, 1, MSG_PEEK);
				if (ret == -1)
				{
					socket_member(socket)->checkError();
					socket->close();
					object_member(socket)->emit(Signal_Disconnected);
				}
				else if (ret == 0)
				{
					socket->close();
					object_member(socket)->emit(Signal_Disconnected);
				}
				else if (ret == 1)
				{
					object_member(socket)->emit(Signal_ReadyRead);
				}
			}
		}
		else if (FD_ISSET(sock, &writefds))
		{
			if (socket_member(socket)->state == BSocket::State_Connecting)
			{
				socket_member(socket)->state = BSocket::State_Connected;
				object_member(socket)->emit(Signal_Connected);
			}
		}
		else if (FD_ISSET(sock, &exceptfds))
		{
			socket_member(socket)->state = BSocket::State_Closed;
			object_member(socket)->emit(Signal_Disconnected);
		}
	}
}

#endif

#ifdef _WIN32
#include <WinSock2.h>

void App_SocketStart()
{
	if (app_sockets.empty())
	{
		WSADATA wsaData;
		WORD wVersionRequested = MAKEWORD(2, 2);
		int err = WSAStartup(wVersionRequested, &wsaData);
		if (err == SOCKET_ERROR)
		{
			perror("socket init failed");
		}
	}
}
void App_SocketClean()
{
	if (app_sockets.empty())
	{
		WSACleanup();
	}
}
void App_SelectSocket(double timeout)
{
	if (app_sockets.empty())
		return;

	fd_set readfds;
	fd_set writefds;
	fd_set exceptfds;

	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&exceptfds);

	BUInt maxsock = 1;
	int count = 0;
	BSocket* sockets[64];
	for (int i = 0; i < app_sockets.size(); i++)
	{
		if (i >= 64)
			break;
		BSocket* socket = app_sockets[i];
		if (socket_member(socket)->state == BSocket::State_Closed)
			continue;
		sockets[count++] = socket;
		SOCKET sock = socket_member(socket)->sock;
		if (sock >= maxsock)
			maxsock = (BUInt)sock + 1;
		FD_SET(sock, &readfds);
		FD_SET(sock, &writefds);
		FD_SET(sock, &exceptfds);
	}
	if (count == 0)
	{
		return;
	}

	timeval tv;
	tv.tv_sec = (long)timeout;
	tv.tv_usec = (long)(timeout - tv.tv_sec * 1000);

	int ret = ::select(maxsock, &readfds, &writefds, &exceptfds, &tv);
	if (ret == -1)
	{
		perror("select");
		return;
	}
	if (ret == 0)
	{
		return;
	}

	for (int i = 0; i < count; i++)
	{
		BSocket* socket = sockets[i];
		SOCKET sock = socket_member(socket)->sock;
		if (FD_ISSET(sock, &readfds))
		{
			if (socket->type() == BSocket::TcpServer)
			{
				sockaddr_in addr;
				memset(&addr, 0, sizeof(addr));
				int addrSize = sizeof(addr);

				SOCKET tcpsock = ::accept(sock, (sockaddr*)&addr, &addrSize);
				if (sock == INVALID_SOCKET)
				{
					socket_member(socket)->checkError();
					socket_member(socket)->state = BSocket::State_Closed;
					object_member(socket)->emit(Signal_StateChanged, BSocket::State_Closed);
				}
				else
				{
					BTcpSocket* tcpSocket = new BTcpSocket();
					socket_member(tcpSocket)->sock = tcpsock;
					socket_member(tcpSocket)->addr = addr;
					{
						char cstr[64];
						inet_ntop(AF_INET, &(addr.sin_addr.S_un.S_addr), cstr, 63);
						int port = ntohs(addr.sin_port);
						socket_member(tcpSocket)->address = cstr;
						socket_member(tcpSocket)->port = ntohs(addr.sin_port);
					}
					socket_member(tcpSocket)->state = BSocket::State_Connected;
					object_member(socket)->emit(Signal_StateChanged, BSocket::State_Connected);
					tcpserver_member(socket)->newConnection = tcpSocket;
					object_member(socket)->emit(Signal_NewConnection, tcpSocket);
				}
			}
			else
			{
				char buf[2];
				int ret = ::recv(sock, buf, 1, MSG_PEEK);
				if (ret == SOCKET_ERROR)
				{
					socket_member(socket)->checkError();
					socket->close();
					object_member(socket)->emit(Signal_Disconnected);
				}
				else if (ret == 0)
				{
					socket->close();
					object_member(socket)->emit(Signal_Disconnected);
				}
				else if (ret == 1)
				{
					object_member(socket)->emit(Signal_ReadyRead);
				}
			}
		}
		else if (FD_ISSET(sock, &writefds))
		{
			if (socket_member(socket)->state == BSocket::State_Connecting)
			{
				socket_member(socket)->state = BSocket::State_Connected;
				object_member(socket)->emit(Signal_Connected);
			}
		}
		else if (FD_ISSET(sock, &exceptfds))
		{
			socket_member(socket)->state = BSocket::State_Closed;
			object_member(socket)->emit(Signal_Disconnected);

		}
	}
}

#endif
